home *** CD-ROM | disk | FTP | other *** search
/ Sun Solutions 1997 April to September / Sun Solutions CD - APR '97 - SEP '97 (704-3778-12 Rev. H)(Sun Microsystems, Inc.)(1997).iso / products / Hyperion / src / display.c < prev    next >
C/C++ Source or Header  |  1997-02-26  |  31KB  |  1,153 lines

  1. /*
  2.  * @(#)display.c    1.65 1/11/94
  3.  *
  4.  * display.c - update the status indicators and other display elements.
  5.  */
  6. static char *ident = "@(#)display.c    1.65 1/11/94";
  7.  
  8. #include <stdio.h>
  9. #include <sys/param.h>
  10. #include <sys/types.h>
  11. #include <xview/xview.h>
  12. #include <xview/panel.h>
  13. #include "workman_ui.h"
  14. #include "struct.h"
  15.  
  16. void    continued();
  17. void    setup_itimer();
  18. void    init_stats();
  19. void    avoid_track();
  20. void    keep_settings();
  21. void    cd_volume();
  22. void    set_default_volume();
  23. char *    listentry();
  24. char *    trackname();
  25. int *    get_playlist();
  26. void    kill_stats();
  27. void    insert_into_playlist();
  28. void    scoot_stuff();
  29. int    switch_playlists();
  30.  
  31. extern Panel_item quitbutton;
  32. extern Rect    *track_rect;
  33. extern int    add_height, small_height, basic_spacing;
  34.  
  35. /*
  36.  * Global object definitions.
  37.  */
  38. extern window1_objects    *Workman_window1;
  39. extern popup1_objects    *Workman_popup1;
  40. extern about_objects    *Workman_about;
  41. extern goodies_objects    *Workman_goodies;
  42. extern plpopup_objects    *Workman_plpopup;
  43.  
  44. extern int num_names, num_nalloc;
  45. extern int cur_track, cur_pos_abs, cur_pos_rel, cur_tracklen, cur_cdlen,
  46.     cur_ntracks, cur_nsections, cur_lasttrack;
  47. extern enum cd_modes cur_cdmode;
  48. extern int cur_frame;
  49. extern char *cur_cdname, *cur_artist;
  50. extern int displayed_track, pop_track, *pop_list, pop_listsize, pl_item,
  51.     pl_listnum;
  52. extern char *empty;
  53. extern int min_lines;
  54. extern int mark_a, mark_b;
  55. extern int manual_volume;
  56. extern int window_is_open;
  57. extern int dont_retry;
  58. extern int dismiss_button;
  59. extern int show_titles;
  60.  
  61. /*
  62.  * Change the icon label. 
  63.  */
  64. void
  65. icon_label(string)
  66.     char    *string;
  67. {
  68.     Xv_opaque    icon;
  69.  
  70.     icon = xv_get(Workman_window1->window1, FRAME_ICON);
  71.     xv_set(icon, ICON_LABEL, string, NULL);
  72.     xv_set(Workman_window1->window1, FRAME_ICON, icon, NULL);
  73. }
  74.  
  75. /*
  76.  * Center the track list.
  77.  */
  78. void
  79. center_tracks()
  80. {
  81.     Rect *tracks;
  82.     int x, cwidth;
  83.     
  84.     cwidth = xv_get(Workman_window1->controls1, XV_WIDTH);
  85.  
  86.     tracks = (Rect *) xv_get(Workman_window1->tracks, PANEL_ITEM_RECT);
  87.     x = (cwidth - tracks->r_width) / 2;
  88.     if (x < 0)
  89.         x = 0;
  90.     xv_set(Workman_window1->tracks, PANEL_ITEM_X, x, NULL);
  91. }
  92.  
  93. static Xv_opaque *tracknames = NULL;
  94.  
  95. /*
  96.  * Center all the read-only titles.
  97.  */
  98. void
  99. center_titles()
  100. {
  101.     Rect *message;
  102.     int x, cwidth, i;
  103.     
  104.     cwidth = xv_get(Workman_window1->controls1, XV_WIDTH);
  105.  
  106.     for (i = 0; num_nalloc && (i < num_nalloc); i++)
  107.         if (tracknames[i] != XV_NULL)
  108.         {
  109.             message = (Rect *) xv_get(tracknames[i],
  110.                             PANEL_ITEM_RECT);
  111.             x = (cwidth - message->r_width) / 2;
  112.             if (x < 0)
  113.                 x = 0;
  114.             xv_set(tracknames[i], PANEL_ITEM_X, x, NULL);
  115.         }
  116. }
  117.  
  118. /*
  119.  * Find a polite place to split up a title line.  Search from right
  120.  * to left for each of these:
  121.  *
  122.  * 1. One of ",;-):]}" unless next char is the same (split point is next char)
  123.  * 2. One of "([{" unless prev char is the same
  124.  * 3. " "
  125.  *
  126.  * If none are found, split at the last character.
  127.  */
  128. int
  129. find_polite_split(name, right)
  130.     char    *name;
  131.     int    right;
  132. {
  133.     int    i;
  134.  
  135.     for (i = right; i >= right/2; i--)
  136.         if ((name[i] == '-' || name[i] == ')' || name[i] == ':' ||
  137.                 name[i] == ']' || name[i] == '}' ||
  138.                 name[i] == ',' || name[i] == ';') &&
  139.                 name[i] != name[i + 1])
  140.             return (i + 1);
  141.     
  142.     for (i = right; i > right/3; i--)
  143.         if ((name[i] == '(' || name[i] == '[' || name[i] == '{') &&
  144.                 name[i] != name[i - 1])
  145.             return (i);
  146.     
  147.     for (i = right; i >= 0; i--)
  148.         if (name[i] == ' ')
  149.             return (i);
  150.     
  151.     return (right);
  152. }
  153.  
  154. /*
  155.  * Put a line of track title onscreen.  If the line is too big to fit,
  156.  * split it up, attempting to do the split in a polite place if possible.
  157.  */
  158. void
  159. set_trackname(y, tnum, name, bold)
  160.     int    *y, *tnum, bold;
  161.     char    *name;
  162. {
  163.     char    c;
  164.     int    i, jump;
  165.     int    maxwidth = (int) xv_get(Workman_window1->controls1, XV_WIDTH) -
  166.             basic_spacing * 2;
  167.  
  168.     if (name == NULL)
  169.         return;
  170.     
  171.     if (*tnum == num_nalloc)
  172.     {
  173.         if (tracknames == NULL)
  174.             tracknames = (Xv_opaque *)malloc(++num_nalloc *
  175.                 sizeof(Xv_opaque));
  176.         else
  177.             tracknames = (Xv_opaque *)realloc(tracknames,
  178.                 ++num_nalloc * sizeof(Xv_opaque));
  179.         if (tracknames == NULL)
  180.         {
  181.             perror("set_trackname: tracknames");
  182.             exit(1);
  183.         }
  184.  
  185.         tracknames[*tnum] = xv_create(Workman_window1->controls1,
  186.             PANEL_MESSAGE, PANEL_ITEM_X, 0,
  187.             XV_SHOW, FALSE, XV_HELP_DATA, "workman:tracknames",
  188.             NULL);
  189.         if (tracknames[*tnum] == XV_NULL)
  190.         {
  191.             perror("set_trackname: xv_create");
  192.             exit(1);
  193.         }
  194.     }
  195.  
  196.     xv_set(tracknames[*tnum], PANEL_LABEL_STRING, name, PANEL_LABEL_BOLD,
  197.         bold, PANEL_ITEM_Y, *y, NULL);
  198.     
  199.     /*
  200.      * Wrap the name if it's too big.
  201.      */
  202.     if ((int) xv_get(tracknames[*tnum], XV_WIDTH) > maxwidth)
  203.     {
  204.         i = (int) strlen(name) / 2;
  205.         jump = i / 2 + 1;
  206.  
  207.         while (jump > 0)
  208.         {
  209.             c = name[i];
  210.             name[i] = '\0';
  211.             xv_set(tracknames[*tnum], PANEL_LABEL_STRING, name,
  212.                 NULL);
  213.             if ((int) xv_get(tracknames[*tnum], XV_WIDTH) <
  214.                                 maxwidth)
  215.             {
  216.                 name[i] = c;
  217.                 i += jump;
  218.             }
  219.             else
  220.             {
  221.                 name[i] = c;
  222.                 i -= jump;
  223.             }
  224.  
  225.             jump /= 2;
  226.         }
  227.  
  228.         i = find_polite_split(name, i - 1);
  229.         c = name[i];
  230.         name[i] = '\0';
  231.         xv_set(tracknames[*tnum], PANEL_LABEL_STRING, name, NULL);
  232.         name[i] = c;
  233.  
  234.         *y += (int) xv_get(Workman_about->message3, XV_HEIGHT) +
  235.             basic_spacing / 2;
  236.         (*tnum)++;
  237.  
  238.         set_trackname(y, tnum, name + i + (c == ' '), bold);
  239.     }
  240.     else
  241.     {
  242.         *y += (int) xv_get(Workman_about->message3, XV_HEIGHT) +
  243.             basic_spacing / 2;
  244.         (*tnum)++;
  245.     }
  246. }
  247.  
  248. /*
  249.  * Preprocess a track name.  Split display lines up into 3 arrays of char*s,
  250.  * one for the CD title, one for the artist, and one for the track title.
  251.  * The program returns the three arrays and the number of elements in each.
  252.  * "spec" should be 0 to disallow special characters (+/@) and only stick
  253.  * entries in the title array.
  254.  *
  255.  * Note: the caller should free each array[0] and array when it's done.
  256.  *
  257.  * This routine is a mess.  It does way, way, way too much malloc()ing.
  258.  * And only fools tread willingly in the realm of the hideous triple pointer.
  259.  */
  260. void
  261. split_trackname(name, artist, nartist, cdname, ncdname, title, ntitle, spec)
  262.     char    *name, ***artist, ***cdname, ***title;
  263.     int    *nartist, *ncdname, *ntitle, spec;
  264. {
  265.     char    *c, *tmpbuf = NULL, ***list;
  266.     int    *nlist;
  267.  
  268.     if (spec)
  269.     {
  270.         *nartist = 0;
  271.         *ncdname = 0;
  272.         *cdname = NULL;
  273.         *artist = NULL;
  274.     }
  275.     *ntitle = 0;
  276.     *title = NULL;
  277.  
  278.     /* Copy the name into a temporary buffer so we can mutilate it */
  279.     strmcpy(&tmpbuf, name);
  280.     name = tmpbuf;
  281.  
  282.     while (*name != '\0')
  283.     {
  284.         if (*name == '+' && spec)
  285.         {
  286.             list = cdname;
  287.             nlist = ncdname;
  288.             name++;
  289.         }
  290.         else if (*name == '@' && spec)
  291.         {
  292.             list = artist;
  293.             nlist = nartist;
  294.             name++;
  295.         }
  296.         else
  297.         {
  298.             list = title;
  299.             nlist = ntitle;
  300.         }
  301.  
  302.         if (*list == NULL)
  303.         {
  304.             /*
  305.              * There can't be more elements than strlen(name)/2+1,
  306.              * since it takes two characters just to delimit an
  307.              * element.  So we allocate that many elements to
  308.              * begin with.
  309.              */
  310.             *list = (char **) malloc(sizeof (char *) *
  311.                 ((int) strlen(name) / 2 + 1));
  312.  
  313.             /*
  314.              * Each array's first element is really a pointer
  315.              * to a bunch of strings, one after the other,
  316.              * comprising that array's elements; the remaining
  317.              * elements point into that buffer.  Since the
  318.              * combined length of those strings can never
  319.              * exceed the remaining length of "name", it's
  320.              * safe to allocate that many characters at the
  321.              * start and not worry about growing the buffer
  322.              * later on.  This also makes freeing all the
  323.              * buffers easier.
  324.              */
  325.             if (*list != NULL)
  326.                 **list = (char *) malloc(strlen(name) + 1);
  327.         }
  328.         else
  329.         {
  330.             /*
  331.              * Add another element to an array.  The new element
  332.              * will point just past the previous one's terminating
  333.              * '\0' (see above).
  334.              */
  335.             (*list)[*nlist] = (*list)[*nlist - 1] +
  336.                     strlen((*list)[*nlist - 1]) + 1;
  337.         }
  338.             
  339.         if (*list == NULL || **list == NULL)
  340.         {
  341.             perror("Couldn't expand list");
  342.             exit(1);
  343.         }
  344.  
  345.         /* Search for a "//". */
  346.         for (c = name; c[0] != '\0'; c++)
  347.             if (c[0] == '/' && c[1] == '/')
  348.                 break;
  349.  
  350.         /* No "//"?  End of the line! */
  351.         if (c[0] == '\0')
  352.         {
  353.             strcpy((*list)[(*nlist)++], name);
  354.             break;
  355.         }
  356.  
  357.         c[0] = '\0';
  358.         strcpy((*list)[(*nlist)++], name);
  359.         name = &c[2];
  360.     }
  361.  
  362.     free(tmpbuf);
  363. }
  364.  
  365. /*
  366.  * Search backwards for a particular line of a title/CD name/artist.
  367.  * Pass '@', '+', or '-' to determine which.
  368.  */
  369. char *
  370. backtitle(track, c, line)
  371.     int    track, line;
  372.     char    c;
  373. {
  374.     int    i, nartist = 0, ncdname = 0, ntrack = 0, *count;
  375.     char    *p = NULL, **artist = NULL, **cdname = NULL, **title, ***list;
  376.     static char *newname = NULL;
  377.  
  378.     if (newname != NULL)
  379.         free(newname);
  380.     newname = NULL;
  381.  
  382.     if (c == '+')
  383.     {
  384.         list = &cdname;
  385.         count = &ncdname;
  386.     }
  387.     else if (c == '@')
  388.     {
  389.         list = &artist;
  390.         count = &ntrack;
  391.     }
  392.     else
  393.     {
  394.         list = &title;
  395.         count = &ntrack;
  396.     }
  397.  
  398.     for (i = track - 2; i > -1; i--)
  399.     {
  400.         split_trackname(trackname(i), &artist, &nartist, &cdname,
  401.             &ncdname, &title, &ntrack, 1);
  402.         
  403.         if (*count <= line)
  404.         {
  405.             p = "";
  406.             break;
  407.         }
  408.  
  409.         if (*list && (*list)[line][0])
  410.         {
  411.             p = (*list)[line];
  412.             break;
  413.         }
  414.  
  415.         if (nartist)
  416.         {
  417.             free(artist[0]);
  418.             free(artist);
  419.         }
  420.         if (ncdname)
  421.         {
  422.             free(cdname[0]);
  423.             free(cdname);
  424.         }
  425.         if (ntrack)
  426.         {
  427.             free(title[0]);
  428.             free(title);
  429.         }
  430.     }
  431.  
  432.     if (p == NULL)
  433.         p = "";
  434.     
  435.     strmcpy(&newname, p);
  436.  
  437.     if (nartist)
  438.     {
  439.         free(artist[0]);
  440.         free(artist);
  441.     }
  442.     if (ncdname)
  443.     {
  444.         free(cdname[0]);
  445.         free(cdname);
  446.     }
  447.     if (ntrack)
  448.     {
  449.         free(title[0]);
  450.         free(title);
  451.     }
  452.  
  453.     return (newname);
  454. }
  455.  
  456. /*
  457.  * Update the track name, using "//" as a line delimiter.  Grow the number
  458.  * of tracks as necessary; never actually get rid of a message object, but
  459.  * rather hide all the unused ones.
  460.  */
  461. void
  462. new_trackname_display(title, track)
  463.     char    *title;
  464.     int    track;
  465. {
  466.     int    tnum = 0, i, new_height, y, nartist, ncdname, ntitle;
  467.     char    **artist, **cdname, **trackname, *scratch;
  468.     static int old_height = 10, max_height = 0;
  469.  
  470.     if (! show_titles)
  471.         return;
  472.  
  473.     if (title == NULL)
  474.         title = show_titles > 0 ? "Unknown track name" : "";
  475.  
  476.     for (i = 0; i < num_nalloc; i++)
  477.         if (tracknames[i] != XV_NULL)
  478.             xv_set(tracknames[i], XV_SHOW, FALSE, NULL);
  479.  
  480.     split_trackname(title, &artist, &nartist, &cdname, &ncdname,
  481.         &trackname, &ntitle, 1);
  482.  
  483.     y = 10;
  484.  
  485.     if (nartist)
  486.         for (i = 0; i < nartist; i++)
  487.             set_trackname(&y, &tnum, artist[i][0] == '\0' ?
  488.                 backtitle(track, '@', i) : artist[i], TRUE);
  489.     else
  490.     {
  491.         scratch = (char *)xv_get(Workman_popup1->artist, PANEL_VALUE);
  492.         if (! scratch[0] && show_titles > 0)
  493.             scratch = "Unknown artist";
  494.         split_trackname(scratch, NULL, NULL, NULL, NULL,
  495.             &artist, &nartist, 0);
  496.         for (i = 0; i < nartist; i++)
  497.             set_trackname(&y, &tnum, artist[i], TRUE);
  498.     }
  499.  
  500.     if (artist != NULL)
  501.     {
  502.         y += basic_spacing / 2;
  503.         free(artist[0]);
  504.         free(artist);
  505.     }
  506.  
  507.     if (ncdname)
  508.         for (i = 0; i < ncdname; i++)
  509.             set_trackname(&y, &tnum, cdname[i][0] == '\0' ?
  510.                 backtitle(track, '+', i) : cdname[i], TRUE);
  511.     else
  512.     {
  513.         scratch = (char *)xv_get(Workman_popup1->cdname, PANEL_VALUE);
  514.         if (! scratch[0] && show_titles > 0)
  515.             scratch = "Unknown CD name";
  516.         split_trackname(scratch, NULL, NULL, NULL, NULL,
  517.             &cdname, &ncdname, 0);
  518.         for (i = 0; i < ncdname; i++)
  519.             set_trackname(&y, &tnum, cdname[i], TRUE);
  520.     }
  521.  
  522.     if (cdname != NULL)
  523.     {
  524.         y += basic_spacing / 2;
  525.         free(cdname[0]);
  526.         free(cdname);
  527.     }
  528.  
  529.     if (ntitle)
  530.     {
  531.         for (i = 0; i < ntitle; i++)
  532.             set_trackname(&y, &tnum,
  533.                 trackname[i][0] == '\0' ?
  534.                 backtitle(track, '-', i) : trackname[i], FALSE);
  535.         y += basic_spacing / 2;
  536.         free(trackname[0]);
  537.         free(trackname);
  538.     }
  539.  
  540.     /*
  541.      * Now figure out how much to grow or shrink the display.  Always
  542.      * grow it if necessary; only shrink it if min_lines (-l option)
  543.      * is >= 0 and there were more than that many title lines before.
  544.      */
  545.     tnum -= 2;    /* cd name and artist don't count at this point */
  546.  
  547.     if (min_lines < 0)
  548.         if (tnum < num_names)
  549.             new_height = old_height;
  550.         else
  551.         {
  552.             new_height = y;
  553.             num_names = tnum;
  554.         }
  555.     else
  556.         if (tnum < min_lines)
  557.         {
  558.             if (show_titles > 0 || min_lines || tnum != -2)
  559.                 new_height = (min_lines + 2) *
  560.                     ((int)xv_get(Workman_about->message3,
  561.                     XV_HEIGHT) + basic_spacing / 2) +
  562.                     (basic_spacing / 2) * 3 + 10;
  563.             else
  564.                 new_height = 10;
  565.             num_names = min_lines;
  566.         }
  567.         else
  568.         {
  569.             new_height = y;
  570.             num_names = tnum;
  571.         }
  572.  
  573.     scoot_stuff(new_height - old_height, 1);
  574.  
  575.     center_titles();
  576.  
  577.     for (i = 0; i < tnum + 2; i++)
  578.         if (tracknames[i] != XV_NULL)    /* which it had better be */
  579.             xv_set(tracknames[i], XV_SHOW, TRUE, NULL);
  580.     
  581.     old_height = new_height;
  582.     if (new_height > max_height)
  583.         max_height = new_height;
  584. }
  585.  
  586. /* A bunch of numbers for button labels. */
  587. char **numbered_buttons = NULL;
  588.  
  589. /*
  590.  * Update the per-track information.  This is called once every time we
  591.  * see we're on a new track.
  592.  */
  593. void
  594. new_track(ip)
  595. window1_objects *ip;
  596. {
  597.     char    scratch[20];
  598.  
  599.     if (cur_track <= 0)
  600.         return;
  601.     new_trackname_display(trackname(cur_track - 1)[0] ?
  602.         trackname(cur_track - 1) : NULL, cur_track);
  603.     xv_set(ip->songpos, PANEL_MIN_VALUE, 0, PANEL_MAX_VALUE, 
  604.         tracklen(cur_track - 1), PANEL_INACTIVE, FALSE, PANEL_VALUE,
  605.         cur_frame < cd->trk[cur_track - 1].start ? 0 : cur_pos_rel,
  606.         NULL);
  607.     sprintf(scratch, "%2d:%02d", tracklen(cur_track - 1) / 60,
  608.         tracklen(cur_track - 1) % 60);
  609.     xv_set(ip->tracklen, PANEL_LABEL_STRING, scratch, NULL);
  610.     xv_set(ip->cdgauge, PANEL_MIN_VALUE, 0, PANEL_MAX_VALUE, get_runtime(),
  611.         PANEL_INACTIVE, FALSE, NULL);
  612.     sprintf(scratch, "%2d:%02d", get_runtime() / 60, get_runtime() % 60);
  613.     xv_set(ip->cdlen, PANEL_LABEL_STRING, scratch, NULL);
  614.     xv_set(ip->tracks, PANEL_VALUE, cur_track - 1, PANEL_CHOICE_STRING,
  615.         cur_track - 1, numbered_buttons[cur_track - 1], NULL);
  616.     xv_set(ip->cdgauge, PANEL_VALUE, cur_pos_abs, NULL);
  617.     xv_set(Workman_goodies->split, PANEL_INACTIVE,
  618.         cd->trk[cur_track - 1].data, NULL);
  619.     xv_set(Workman_goodies->delete, PANEL_INACTIVE,
  620.         cd->trk[cur_track - 1].section < 2, NULL);
  621.     sprintf(scratch, "Track %d", cd->trk[cur_track - 1].track);
  622.     if (cd->trk[cur_track - 1].section)
  623.         sprintf(scratch + strlen(scratch), ".%d",
  624.             cd->trk[cur_track - 1].section);
  625.     icon_label(scratch);
  626.     figure_volume(ip);
  627.     displayed_track = cur_track;
  628. }
  629.  
  630. /*
  631.  * Update all the moving status indicators.
  632.  */
  633. void
  634. show_stats(ip)
  635. window1_objects *ip;
  636. {
  637.     static char    trk_time[6], abs_time[6];
  638.     static enum cd_modes    old_cdmode = UNKNOWN;
  639.     int        pos;
  640.  
  641. /* If we're on a different track than we used to be, update the track info */
  642.     if (displayed_track != cur_track && cur_cdmode != EJECTED)
  643.         new_track(ip);
  644.  
  645. /* Update the current play mode */
  646.     if (old_cdmode != cur_cdmode)
  647.     {
  648.         old_cdmode = cur_cdmode;
  649.         xv_set(ip->mode, PANEL_VALUE, cur_cdmode, NULL);
  650.     }
  651.  
  652.     if (! window_is_open)
  653.         return;
  654.  
  655. /* Update the track timer and slider */
  656.     if (displayed_track == -1)
  657.         cur_tracklen = cur_cdlen;
  658.  
  659.     if (xv_get(Workman_goodies->timemode_track, PANEL_VALUE) == 0)
  660.         if (cur_track > 0 && cur_frame < cd->trk[cur_track - 1].start)
  661.             (void) sprintf(trk_time, "-%1d:%02d", cur_pos_rel / 60,
  662.                 cur_pos_rel % 60);
  663.         else
  664.             (void) sprintf(trk_time, "%02d:%02d", cur_pos_rel / 60,
  665.                 cur_pos_rel % 60);
  666.     else
  667.     {
  668.         pos = tracklen(cur_track - 1) - cur_pos_rel;
  669.         if (pos < 0)    /* transitioning between tracks... */
  670.             pos = 0;
  671.         (void) sprintf(trk_time, "%02d:%02d", pos / 60, pos % 60);
  672.     }
  673.  
  674.     if (xv_get(Workman_goodies->timemode_cd, PANEL_VALUE) == 0)
  675.         (void) sprintf(abs_time, "%02d:%02d", cur_pos_abs / 60,
  676.             cur_pos_abs % 60);
  677.     else
  678.         (void) sprintf(abs_time, "%02d:%02d", (get_runtime() -
  679.             cur_pos_abs) / 60, (get_runtime() - cur_pos_abs) % 60);
  680.  
  681.     if (strcmp(trk_time, (char *)xv_get(ip->tracktimer,PANEL_LABEL_STRING)))
  682.         xv_set(ip->tracktimer, PANEL_LABEL_STRING, trk_time, NULL);
  683.     if ((cur_pos_rel % 5) == 0 && xv_get(ip->songpos, PANEL_VALUE) !=
  684.                                 cur_pos_rel)
  685.         xv_set(ip->songpos, PANEL_VALUE, (cur_track > 0 && cur_frame <
  686.             cd->trk[cur_track - 1].start) ? 0 : cur_pos_rel, NULL);
  687.  
  688. /* Update the CD gauge */
  689.     if (strcmp(abs_time, (char *)xv_get(ip->cdtimer, PANEL_LABEL_STRING)))
  690.         xv_set(ip->cdtimer, PANEL_LABEL_STRING, abs_time, NULL);
  691.     if ((cur_pos_rel % 10) == 0 && xv_get(ip->cdgauge, PANEL_VALUE) !=
  692.                                 cur_pos_abs)
  693.         xv_set(ip->cdgauge, PANEL_VALUE, cur_pos_abs, NULL);
  694.  
  695. /* Are we past the beginning of the a-b repeat block? */
  696.     if (mark_a && cur_frame > mark_a)
  697.     {
  698.         if (xv_get(Workman_goodies->b, PANEL_INACTIVE) == TRUE)
  699.             xv_set(Workman_goodies->b, PANEL_INACTIVE, FALSE, NULL);
  700.     }
  701.     else
  702.         if (xv_get(Workman_goodies->b, PANEL_INACTIVE) == FALSE)
  703.         {
  704.             xv_set(Workman_goodies->b, PANEL_INACTIVE, TRUE, NULL);
  705.             if (! xv_get(Workman_goodies->abrepeat, PANEL_VALUE))
  706.                 xv_set(Workman_goodies->blabel, PANEL_INACTIVE,
  707.                     TRUE, NULL);
  708.         }
  709.  
  710.     if (xv_get(Workman_goodies->a, PANEL_INACTIVE) ==
  711.         (cur_cdmode == PLAYING || cur_cdmode == PAUSED))
  712.     {
  713.         xv_set(Workman_goodies->a, PANEL_INACTIVE,
  714.                cur_cdmode != PLAYING && cur_cdmode != PAUSED, NULL);
  715.         if (cur_cdmode != PLAYING && cur_cdmode != PAUSED)
  716.             xv_set(Workman_goodies->b, PANEL_INACTIVE, TRUE, NULL);
  717.     }
  718. }
  719.  
  720. /*
  721.  * Populate the numbered buttons.  This has to be done in a fairly stupid
  722.  * manner since XView doesn't copy choice strings to its own buffers.
  723.  */
  724. void
  725. fill_buttons()
  726. {
  727.     Xv_opaque t = Workman_window1->tracks;
  728.     int    i, oldheight;
  729.     char    temp[20];
  730.  
  731.     xv_set(t, XV_SHOW, FALSE, NULL);
  732.  
  733.     if (numbered_buttons != NULL)
  734.     {
  735.         for (i = 0; numbered_buttons[i]; i++)
  736.             free(numbered_buttons[i]);
  737.         free(numbered_buttons);
  738.     }
  739.  
  740.     numbered_buttons = (char **) calloc(cur_ntracks + 1, sizeof(char **));
  741.     if (numbered_buttons == NULL)
  742.     {
  743.         perror("fill_buttons");
  744.         exit(1);
  745.     }
  746.  
  747.     track_rect = (Rect *) xv_get(t, PANEL_ITEM_RECT);
  748.     oldheight = track_rect->r_height;
  749.  
  750.     xv_set(t, PANEL_CHOICE_STRINGS, " ", NULL, NULL);
  751.  
  752.     for (i = 0; i < cur_ntracks; i++)
  753.     {
  754.         if (cd->trk[i].section)
  755.             sprintf(temp, "%2d.%d", cd->trk[i].track,
  756.                 cd->trk[i].section);
  757.         else
  758.             sprintf(temp, "%*d", cur_nsections ? 3 : 2,
  759.                 cd->trk[i].track);
  760.         
  761.         numbered_buttons[i] = (char *) malloc(strlen(temp) + 1);
  762.         if (numbered_buttons[i] == NULL)
  763.         {
  764.             perror("fill_buttons");
  765.             exit(1);
  766.         }
  767.         strcpy(numbered_buttons[i], temp);
  768.  
  769.         xv_set(t, PANEL_CHOICE_STRING, i, numbered_buttons[i], NULL);
  770.     }
  771.  
  772.     numbered_buttons[i] = NULL;
  773.  
  774.     xv_set(t, PANEL_CHOICE_NROWS, 1, NULL);
  775.     i = 1;
  776.     while (xv_get(t, XV_WIDTH) + 5 >= xv_get(Workman_window1->controls1,
  777.                                 XV_WIDTH))
  778.         xv_set(t, PANEL_CHOICE_NROWS, ++i, NULL);
  779.  
  780.     center_tracks();
  781.  
  782.     track_rect = (Rect *) xv_get(t, PANEL_ITEM_RECT);
  783.     add_height = track_rect->r_height - small_height;
  784.     scoot_stuff(track_rect->r_height - oldheight, 0);
  785.  
  786.     xv_set(t, XV_SHOW, TRUE, NULL);
  787. }
  788.  
  789. /*
  790.  * Set the CD length gauge to the right length for the whole CD.
  791.  */
  792. void
  793. reset_cdlen(ip)
  794. window1_objects *ip;
  795. {
  796.     char    scratch[16];
  797.  
  798.     xv_set(ip->cdgauge, PANEL_MIN_VALUE, 0, PANEL_MAX_VALUE, get_runtime(),
  799.         PANEL_INACTIVE, FALSE, NULL);
  800.     sprintf(scratch, "%2d:%02d", get_runtime() / 60, get_runtime() % 60);
  801.     xv_set(ip->cdlen, PANEL_LABEL_STRING, scratch, NULL);
  802. }
  803.  
  804. /*
  805.  * Fill up the track scrolling list and the track number menu.
  806.  */
  807. void
  808. fill_lists()
  809. {
  810.     popup1_objects    *pu = Workman_popup1;
  811.     plpopup_objects    *pl = Workman_plpopup;
  812.     int i;
  813.  
  814.     xv_set(pu->tracklist, XV_SHOW, FALSE, NULL);
  815.     for (i = 0; i < cur_ntracks; i++)
  816.     {
  817.         xv_set(xv_get(pl->playlist, PANEL_ITEM_MENU), MENU_APPEND_ITEM,
  818.             xv_create(XV_NULL, MENUITEM, MENU_NOTIFY_PROC,
  819.             insert_into_playlist, MENU_RELEASE,
  820.             MENU_STRING, numbered_buttons[i], XV_KEY_DATA, 1234, i +
  821.             1, NULL), NULL);
  822.         xv_set(pu->tracklist, PANEL_LIST_INSERT, i, NULL);
  823.         xv_set(pu->tracklist, PANEL_LIST_STRING, i, listentry(i), NULL);
  824.     }
  825.  
  826.     xv_set(pu->tracklist, XV_SHOW, TRUE, NULL);
  827.  
  828.     xv_set(xv_get(pl->playlist, PANEL_ITEM_MENU), MENU_NCOLS,
  829.         xv_get(Workman_window1->tracks, PANEL_CHOICE_NROWS), NULL);
  830. }
  831.  
  832. /*
  833.  * Initialize all the status indicators (a new CD has been inserted.)
  834.  * This only initializes the static values; the rest are done by show_stats().
  835.  */
  836. void
  837. init_stats(ip)
  838. window1_objects *ip;
  839. {
  840.     popup1_objects    *pu = Workman_popup1;
  841.     plpopup_objects    *pl = Workman_plpopup;
  842.     int i;
  843.     Panel_setting update_title();
  844.  
  845.     reset_cdlen(ip);
  846.     xv_set(ip->mode, PANEL_VALUE, 4, NULL);
  847.     icon_label("Stop");
  848.     xv_set(pu->artist, PANEL_VALUE, cur_artist, PANEL_INACTIVE, FALSE,
  849.         NULL);
  850.     xv_set(pu->cdname, PANEL_VALUE, cur_cdname, PANEL_INACTIVE, FALSE,
  851.         NULL);
  852.     for (i = 0; i < num_nalloc; i++)
  853.         if (tracknames[i] != XV_NULL)
  854.             xv_set(tracknames[i], PANEL_INACTIVE, FALSE, NULL);
  855.     xv_set(ip->volume, PANEL_INACTIVE, FALSE, NULL);
  856.     xv_set(ip->songpos, PANEL_INACTIVE, FALSE, NULL);
  857.     update_title(pu->cdname, NULL);
  858.     xv_set(pu->tracklist, PANEL_INACTIVE, FALSE, NULL);
  859.     xv_set(pu->trackname, PANEL_INACTIVE, FALSE, NULL);
  860.     xv_set(pu->trackoptions, PANEL_INACTIVE, FALSE, NULL);
  861.     xv_set(pu->autoplay, PANEL_INACTIVE, FALSE, NULL);
  862.     xv_set(pl->playlist, PANEL_INACTIVE, FALSE, NULL);
  863.     xv_set(pl->playlists, PANEL_INACTIVE, FALSE, NULL);
  864.     xv_set(pl->listname, PANEL_INACTIVE, FALSE, NULL);
  865.     xv_set(pl->button7, PANEL_INACTIVE, FALSE, NULL);
  866.     xv_set(pu->whichvolume, PANEL_INACTIVE, FALSE, NULL);
  867.     xv_set(pu->defaultvolume, PANEL_INACTIVE, FALSE, NULL);
  868.     xv_set(pu->defaultspeaker, PANEL_INACTIVE, FALSE, NULL);
  869.     xv_set(Workman_goodies->balance, PANEL_INACTIVE, FALSE, NULL);
  870.     xv_set(Workman_goodies->indexscan, PANEL_INACTIVE, FALSE, NULL);
  871.     xv_set(pu->playmode, PANEL_INACTIVE, FALSE, NULL);
  872.     xv_set(pu->button1, PANEL_INACTIVE, FALSE, NULL);
  873.     xv_set(pu->button8, PANEL_INACTIVE, FALSE, NULL);
  874.     fill_buttons(ip);
  875.     fill_lists();
  876.  
  877.     xv_set(ip->tracks, PANEL_CHOOSE_ONE, TRUE, PANEL_CHOOSE_NONE, TRUE,
  878.         PANEL_VALUE, -1, NULL);
  879.     xv_set(pu->whichvolume, PANEL_VALUE, 0, PANEL_DEFAULT_VALUE, 1, NULL);
  880.     xv_set(pu->defaultvolume, PANEL_VALUE, get_default_volume(0), NULL);
  881.     set_default_volume(pu->defaultvolume, get_default_volume(0), NULL);
  882.     xv_set(pu->tracklist, PANEL_LIST_SELECT, 0, TRUE, NULL);
  883.     update_trackname(pu->trackname, NULL, trackname(0),
  884.         PANEL_LIST_OP_SELECT, NULL, 0);
  885.  
  886.     if (cd->lists != NULL)
  887.     {
  888.         for (i = 0; cd->lists[i].name != NULL; i++)
  889.         {
  890.             xv_set(ip->shuffle, PANEL_CHOICE_STRING, i + 2,
  891.                 cd->lists[i].name, NULL);
  892.             xv_set(pl->playlists, PANEL_LIST_INSERT, i,
  893.                 PANEL_LIST_STRING, i, cd->lists[i].name, NULL);
  894.         }
  895.  
  896.         (void) switch_playlists(pl->playlists, NULL, NULL,
  897.             PANEL_LIST_OP_SELECT, NULL, 0);
  898.     }
  899.     else
  900.         pop_listsize = 0;
  901.  
  902.     xv_set(ip->shuffle, PANEL_VALUE, get_playmode(), PANEL_DEFAULT_VALUE,
  903.         (get_playmode() + 1) % (cd->lists == NULL ? 2 : i + 2), NULL);
  904.     next_playmode_default(ip->shuffle, get_playmode(), NULL);
  905.  
  906.     xv_set(pu->autoplay, PANEL_VALUE, get_autoplay(), NULL);
  907.     xv_set(pu->playmode, PANEL_VALUE, get_playmode(), NULL);
  908.  
  909.     displayed_track = -1;
  910.     cur_cdmode = STOPPED;
  911. }
  912.  
  913. /*
  914.  * Clean out the lists/menus that contain track numbers.
  915.  */
  916. void
  917. cleanout_lists()
  918. {
  919.     popup1_objects    *pu = Workman_popup1;
  920.     plpopup_objects    *pl = Workman_plpopup;
  921.     Menu            m;
  922.     int i;
  923.  
  924.     xv_set(pu->tracklist, XV_SHOW, FALSE, NULL);
  925.     i = xv_get(pu->tracklist, PANEL_LIST_NROWS);
  926.     m = (Menu) xv_get(pl->playlist, PANEL_ITEM_MENU);
  927.     while (--i > -1)
  928.     {
  929.         xv_set(m, MENU_REMOVE, i + 2, NULL);
  930.         xv_set(pu->tracklist, PANEL_LIST_DELETE, i, NULL);
  931.     }
  932.     xv_set(pu->tracklist, XV_SHOW, TRUE, NULL);
  933. }
  934.  
  935. /*
  936.  * CD has been ejected.  Remove all the status information.
  937.  */
  938. void
  939. kill_stats(ip)
  940. window1_objects *ip;
  941. {
  942.     popup1_objects    *pu = Workman_popup1;
  943.     plpopup_objects    *pl = Workman_plpopup;
  944.     int        i;
  945.  
  946.     /* If stats are already killed, don't kill 'em again */
  947.     if (xv_get(ip->songpos, PANEL_INACTIVE) == TRUE)
  948.         return;
  949.  
  950.     xv_set(ip->songpos, PANEL_INACTIVE, TRUE, PANEL_VALUE, 0, NULL);
  951.     xv_set(ip->cdgauge, PANEL_INACTIVE, TRUE, PANEL_VALUE, 0, NULL);
  952.     xv_set(pu->artist, PANEL_INACTIVE, TRUE, PANEL_VALUE, "No artist",
  953.         NULL);
  954.     xv_set(pu->cdname, PANEL_INACTIVE, TRUE, PANEL_VALUE, "No CD name",
  955.         NULL);
  956.     new_trackname_display("", 0);
  957.     for (i = 0; i < num_nalloc; i++)
  958.         if (tracknames[i] != XV_NULL)
  959.             xv_set(tracknames[i], PANEL_INACTIVE, TRUE, NULL);
  960.  
  961.     xv_set(ip->volume, PANEL_INACTIVE, TRUE, NULL);
  962.  
  963.     icon_label("No CD");
  964.  
  965.     cleanout_lists();
  966.  
  967.     xv_set(pl->playlist, XV_SHOW, FALSE, NULL);
  968.     i = xv_get(pl->playlist, PANEL_LIST_NROWS);
  969.     while (--i > -1)
  970.         xv_set(pl->playlist, PANEL_LIST_DELETE, i, NULL);
  971.     xv_set(pl->playlist, XV_SHOW, TRUE, NULL);
  972.  
  973.     i = xv_get(pl->playlists, PANEL_LIST_NROWS);
  974.     while (--i > -1)
  975.     {
  976.         xv_set(pl->playlists, PANEL_LIST_DELETE, i, NULL);
  977.     }
  978.  
  979.     xv_set(pu->tracklist, PANEL_INACTIVE, TRUE, NULL);
  980.     xv_set(pu->trackname, PANEL_INACTIVE, TRUE, PANEL_VALUE, empty, NULL);
  981.     xv_set(pu->trackoptions, PANEL_INACTIVE, TRUE, NULL);
  982.     xv_set(pu->autoplay, PANEL_INACTIVE, TRUE, NULL);
  983.     xv_set(pl->playlist, PANEL_INACTIVE, TRUE, NULL);
  984.     xv_set(pl->playlists, PANEL_INACTIVE, TRUE, NULL);
  985.     xv_set(pl->listname, PANEL_INACTIVE, TRUE, NULL);
  986.     xv_set(pl->delete, PANEL_INACTIVE, TRUE, NULL);
  987.     xv_set(pl->button5, PANEL_INACTIVE, TRUE, NULL);
  988.     xv_set(pl->button6, PANEL_INACTIVE, TRUE, NULL);
  989.     xv_set(pl->button7, PANEL_INACTIVE, TRUE, NULL);
  990.     xv_set(Workman_goodies->balance, PANEL_INACTIVE, TRUE, NULL);
  991.     xv_set(Workman_goodies->a, PANEL_INACTIVE, TRUE, NULL);
  992.     xv_set(Workman_goodies->b, PANEL_INACTIVE, TRUE, NULL);
  993.     xv_set(Workman_goodies->alabel, PANEL_INACTIVE, TRUE, NULL);
  994.     xv_set(Workman_goodies->blabel, PANEL_INACTIVE, TRUE, NULL);
  995.     xv_set(Workman_goodies->abrepeat, PANEL_INACTIVE, TRUE, NULL);
  996.     xv_set(Workman_goodies->split, PANEL_INACTIVE, TRUE, NULL);
  997.     xv_set(Workman_goodies->delete, PANEL_INACTIVE, TRUE, NULL);
  998.     xv_set(Workman_goodies->indexscan, PANEL_INACTIVE, TRUE, NULL);
  999.     xv_set(pu->whichvolume, PANEL_INACTIVE, TRUE, NULL);
  1000.     xv_set(pu->defaultvolume, PANEL_INACTIVE, TRUE, NULL);
  1001.     xv_set(pu->defaultspeaker, PANEL_INACTIVE, TRUE, NULL);
  1002.     xv_set(pu->playmode, PANEL_INACTIVE, TRUE, NULL);
  1003.     xv_set(pu->button1, PANEL_INACTIVE, TRUE, NULL);
  1004.     xv_set(pu->button8, PANEL_INACTIVE, TRUE, NULL);
  1005.     xv_set(ip->tracks, PANEL_CHOICE_STRINGS, "No CD in drive", NULL, NULL);
  1006.     xv_set(ip->tracktimer, PANEL_LABEL_STRING, "00:00", NULL);
  1007.     xv_set(ip->cdtimer, PANEL_LABEL_STRING, "00:00", NULL);
  1008.     xv_set(ip->tracklen, PANEL_LABEL_STRING, "0:00", NULL);
  1009.     xv_set(ip->cdlen, PANEL_LABEL_STRING, "0:00", NULL);
  1010.     xv_set(ip->shuffle, PANEL_CHOICE_STRINGS, "Normal", "Shuffle",
  1011.         NULL, PANEL_VALUE, 0, PANEL_DEFAULT_VALUE, 1, NULL);
  1012.     center_tracks();
  1013.  
  1014.     if (track_rect != NULL)
  1015.         scoot_stuff(-add_height, 0);
  1016.  
  1017.     pop_track = 0;
  1018.     pl_item = -1;
  1019.     displayed_track = 0;
  1020.     mark_a = mark_b = 0;
  1021.     manual_volume = 0;
  1022. }
  1023.  
  1024. /*
  1025.  * Event interpose function.  Update window_is_open and a bunch of the
  1026.  * quiescent displays.  Check for a CD if we aren't polling the player
  1027.  * and there wasn't a CD before.
  1028.  */
  1029. Notify_value
  1030. check_open(f, event, arg, type)
  1031.     Frame            f;
  1032.     Notify_event        event;
  1033.     Notify_arg        arg;
  1034.     Notify_event_type     type;
  1035. {
  1036.     Notify_value    val;
  1037.     int        was_opened = window_is_open;
  1038.     int        old_retry;
  1039.  
  1040.     val = (Notify_value) notify_next_event_func(f, event, arg, type);
  1041.     window_is_open = ! xv_get(f, FRAME_CLOSED);
  1042.  
  1043.     if (window_is_open && ! was_opened)
  1044.     {
  1045.         show_stats(Workman_window1);
  1046.         xv_set(Workman_window1->cdgauge, PANEL_VALUE, cur_pos_abs,
  1047.             NULL);
  1048.         xv_set(Workman_window1->songpos, PANEL_VALUE, cur_pos_rel,
  1049.             NULL);
  1050.  
  1051.         if (dont_retry && cur_cdmode == EJECTED)
  1052.         {
  1053.             old_retry = dont_retry;
  1054.             dont_retry = 0;
  1055.             handle_timer(Workman_window1->window1, 0);
  1056.             dont_retry = old_retry;
  1057.         }
  1058.     }
  1059.  
  1060.     return (val);
  1061. }
  1062.  
  1063. /*
  1064.  * Scoot stuff in the main window up or down, as appropriate.  Pass a code for
  1065.  * the topmost thing to scoot (0 = sliders, 1 = tracks) and the distance to
  1066.  * scoot, with negative meaning scoot up.
  1067.  */
  1068. void
  1069. scoot_stuff(distance, topmost)
  1070.     int    distance, topmost;
  1071. {
  1072. #define ip Workman_window1
  1073.     if (distance < 0)
  1074.     {
  1075.         xv_set(ip->window1, XV_HEIGHT, xv_get(ip->window1,
  1076.             XV_HEIGHT) + distance, NULL);
  1077.         if (topmost >= 1)
  1078.             xv_set(ip->tracks, PANEL_ITEM_Y, xv_get(ip->tracks,
  1079.                 PANEL_ITEM_Y) + distance, NULL);
  1080.         xv_set(ip->tracktimer, PANEL_ITEM_Y, xv_get(ip->tracktimer,
  1081.             PANEL_ITEM_Y) + distance, NULL);
  1082.         xv_set(ip->songpos, PANEL_ITEM_Y, xv_get(ip->songpos,
  1083.             PANEL_ITEM_Y) + distance, NULL);
  1084.         xv_set(ip->tracklen, PANEL_ITEM_Y, xv_get(ip->tracklen,
  1085.             PANEL_ITEM_Y) + distance, NULL);
  1086.         xv_set(ip->speaker, PANEL_ITEM_Y, xv_get(ip->speaker,
  1087.             PANEL_ITEM_Y) + distance, NULL);
  1088.         xv_set(ip->volume, PANEL_ITEM_Y, xv_get(ip->volume,
  1089.             PANEL_ITEM_Y) + distance, NULL);
  1090.         xv_set(ip->mode, PANEL_ITEM_Y, xv_get(ip->mode,
  1091.             PANEL_ITEM_Y) + distance, NULL);
  1092.         xv_set(ip->repeat, PANEL_ITEM_Y, xv_get(ip->repeat,
  1093.             PANEL_ITEM_Y) + distance, NULL);
  1094.         xv_set(ip->shuffle, PANEL_ITEM_Y, xv_get(ip->shuffle,
  1095.             PANEL_ITEM_Y) + distance, NULL);
  1096.         xv_set(ip->cdtimer, PANEL_ITEM_Y, xv_get(ip->cdtimer,
  1097.             PANEL_ITEM_Y) + distance, NULL);
  1098.         xv_set(ip->cdgauge, PANEL_ITEM_Y, xv_get(ip->cdgauge,
  1099.             PANEL_ITEM_Y) + distance, NULL);
  1100.         xv_set(ip->cdlen, PANEL_ITEM_Y, xv_get(ip->cdlen,
  1101.             PANEL_ITEM_Y) + distance, NULL);
  1102.         xv_set(ip->button4, PANEL_ITEM_Y, xv_get(ip->button4,
  1103.             PANEL_ITEM_Y) + distance, NULL);
  1104.         xv_set(ip->button3, PANEL_ITEM_Y, xv_get(ip->button3,
  1105.             PANEL_ITEM_Y) + distance, NULL);
  1106.         xv_set(ip->button2, PANEL_ITEM_Y, xv_get(ip->button2,
  1107.             PANEL_ITEM_Y) + distance, NULL);
  1108.         if (dismiss_button)
  1109.             xv_set(quitbutton, PANEL_ITEM_Y, xv_get(quitbutton,
  1110.                 PANEL_ITEM_Y) + distance, NULL);
  1111.     }
  1112.     else if (distance > 0)
  1113.     {
  1114.         xv_set(ip->window1, XV_HEIGHT, xv_get(ip->window1,
  1115.             XV_HEIGHT) + distance, NULL);
  1116.         if (dismiss_button)
  1117.             xv_set(quitbutton, PANEL_ITEM_Y, xv_get(quitbutton,
  1118.                 PANEL_ITEM_Y) + distance, NULL);
  1119.         xv_set(ip->button4, PANEL_ITEM_Y, xv_get(ip->button4,
  1120.             PANEL_ITEM_Y) + distance, NULL);
  1121.         xv_set(ip->button3, PANEL_ITEM_Y, xv_get(ip->button3,
  1122.             PANEL_ITEM_Y) + distance, NULL);
  1123.         xv_set(ip->button2, PANEL_ITEM_Y, xv_get(ip->button2,
  1124.             PANEL_ITEM_Y) + distance, NULL);
  1125.         xv_set(ip->cdtimer, PANEL_ITEM_Y, xv_get(ip->cdtimer,
  1126.             PANEL_ITEM_Y) + distance, NULL);
  1127.         xv_set(ip->cdgauge, PANEL_ITEM_Y, xv_get(ip->cdgauge,
  1128.             PANEL_ITEM_Y) + distance, NULL);
  1129.         xv_set(ip->cdlen, PANEL_ITEM_Y, xv_get(ip->cdlen,
  1130.             PANEL_ITEM_Y) + distance, NULL);
  1131.         xv_set(ip->volume, PANEL_ITEM_Y, xv_get(ip->volume,
  1132.             PANEL_ITEM_Y) + distance, NULL);
  1133.         xv_set(ip->speaker, PANEL_ITEM_Y, xv_get(ip->speaker,
  1134.             PANEL_ITEM_Y) + distance, NULL);
  1135.         xv_set(ip->mode, PANEL_ITEM_Y, xv_get(ip->mode,
  1136.             PANEL_ITEM_Y) + distance, NULL);
  1137.         xv_set(ip->shuffle, PANEL_ITEM_Y, xv_get(ip->shuffle,
  1138.             PANEL_ITEM_Y) + distance, NULL);
  1139.         xv_set(ip->repeat, PANEL_ITEM_Y, xv_get(ip->repeat,
  1140.             PANEL_ITEM_Y) + distance, NULL);
  1141.         xv_set(ip->tracktimer, PANEL_ITEM_Y, xv_get(ip->tracktimer,
  1142.             PANEL_ITEM_Y) + distance, NULL);
  1143.         xv_set(ip->songpos, PANEL_ITEM_Y, xv_get(ip->songpos,
  1144.             PANEL_ITEM_Y) + distance, NULL);
  1145.         xv_set(ip->tracklen, PANEL_ITEM_Y, xv_get(ip->tracklen,
  1146.             PANEL_ITEM_Y) + distance, NULL);
  1147.         if (topmost >= 1)
  1148.             xv_set(ip->tracks, PANEL_ITEM_Y, xv_get(ip->tracks,
  1149.                 PANEL_ITEM_Y) + distance, NULL);
  1150.     }
  1151. }
  1152. #undef ip
  1153.